#include <iostream>
#include <cstdint>

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <thread>

#include "protocol/include/mirp.hpp"
#include "protocol/include/log.hpp"
#include "protocol/include/Market.hpp"

extern Market::Market& getMarket();

using namespace std;


// udp 组播 receiver

void udp_receiver(const int port, const char *municast_addr)
{
    int sock_fd = socket(AF_INET, SOCK_DGRAM, 0);
    if (sock_fd < 0)
    {
        perror("socket");
        exit(1);
    }

    struct sockaddr_in addr;
    memset(&addr, 0, sizeof(addr));
    addr.sin_family = AF_INET;
    addr.sin_port = htons(port);
    addr.sin_addr.s_addr = htonl(INADDR_ANY);

    int ret = bind(sock_fd, (struct sockaddr *)&addr, sizeof(addr));
    if (ret < 0)
    {
        perror("bind");
        exit(1);
    }

    // 组播结构
    struct ip_mreq mreq;
    mreq.imr_multiaddr.s_addr = inet_addr(municast_addr);
    mreq.imr_interface.s_addr = htonl(INADDR_ANY);

    // 加入组播
    ret = setsockopt(sock_fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq));
    if (ret < 0)
    {
        perror("setsockopt");
        exit(1);
    }

    struct sockaddr_in sender;
    socklen_t sender_len = sizeof(sender);

    cout << "waiting for data..." << endl;
    std::vector<uint8_t> buf(409600);
    while (true)
    {
        ret = recvfrom(sock_fd, buf.data(), buf.size(), 0, (struct sockaddr *)&sender, &sender_len);
        if (ret < 0)
        {
            perror("recvfrom");
            exit(1);
        }
        // cout << "ip: " << inet_ntoa(sender.sin_addr) << endl;
        // cout << "port: " << ntohs(sender.sin_port) << endl;
        // printf("data: %s\n", buf);
        MIRPHeader *mirp_header = (MIRPHeader *)buf.data();
        getMarket().parseMIRP(mirp_header);
    }

    // 推出组播
    setsockopt(sock_fd, IPPROTO_IP, IP_DROP_MEMBERSHIP, &mreq, sizeof(mreq));

    close(sock_fd);
    return;
}

int udp_client()
{
    const char *municast_addr = "224.9.9.9";
    udp_receiver(9013, municast_addr);
    return 0;
}
